package com.redxiii.tracplus.ejb.session;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Daniel Filgueiras
 * @since 19/08/2011
 * @param <T>
 */
public abstract class HibernateDaoImpl<T> implements JPADao<T> {

	private static final Logger logger = LoggerFactory.getLogger(HibernateDaoImpl.class);
	
	private Session session;
	private Class<T> clazz;
	
	@SuppressWarnings("unchecked")
	public HibernateDaoImpl() {
		Type type = this.getClass().getGenericSuperclass();
		
		if (!(type instanceof ParameterizedType))
			throw new ExceptionInInitializerError("Invalid super-class type: " + type);
		
		ParameterizedType parameterizedType = (ParameterizedType)type;
		if (parameterizedType.getActualTypeArguments().length != 1)
			throw new ExceptionInInitializerError("No parameterized type for class: " + type);
		
		clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0];
		session = HibernateUtil.getSession();
	}
	
	/* (non-Javadoc)
	 * @see mobi.v2com.tools.tracvote.session.HibernateDao#save(T)
	 */
	public boolean save(T object) {
		Transaction ts = null;
		try {
			ts = session.beginTransaction();
			session.save(object);
			ts.commit();
			return true;
		} catch(HibernateException e) {
			logger.error("Could not perform database operation",e);
			
			if (ts != null) try { ts.rollback(); } catch(Exception ex){}
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see mobi.v2com.tools.tracvote.session.HibernateDao#remote(T)
	 */
	public boolean remote(T object) {
		Transaction ts = null;
		try {
			logger.trace("Deleting object: '{}'", object);
			ts = session.beginTransaction();
			session.delete(object);
			ts.commit();
			return true;
		} catch(HibernateException e) {
			logger.error("Could not perform database operation",e);
			
			if (ts != null) try { ts.rollback(); } catch(Exception ex){}
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see mobi.v2com.tools.tracvote.session.HibernateDao#update(T)
	 */
	public boolean update(T object) {
		Transaction ts = null;
		try {
			ts = session.beginTransaction();
			session.update(object);
			ts.commit();
			return true;
		} catch(HibernateException e) {
			logger.error("Could not perform database operation",e);
			
			if (ts != null) try { ts.rollback(); } catch(Exception ex){}
		}
		return false;
	}
	
	@SuppressWarnings("unchecked")
	public T loadById(Serializable id) {
		return (T) session.load(clazz, id);
	}
	
	@SuppressWarnings("unchecked")
	public List<T> loadAll() {
		return createCriteria().list();
	}
	
	protected Criteria createCriteria() {
		return session.createCriteria(clazz);
	}
	
	protected Criteria createCriteria(Class<?> clazz) {
		return session.createCriteria(clazz);
	}
	
	/**
	 * @param propertyName 
	 * @param value(EQUALS)
	 * @param orderFields (ASC)
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<T> getByProperty(String propertyName, String value, String... orderFields) { 
		Criteria criteria = createCriteria()
			.add(Restrictions.eq(propertyName, value));
		
		for (String order : orderFields) {
			criteria.addOrder(Order.asc(order));
		}
		return criteria.list();
	}
}
